#ifndef __SDBUS_CPP_H__
#define __SDBUS_CPP_H__

#include "BaseType.h"
#include "Tracer.h"
#include "systemd/sd-bus.h"
using namespace libemb;

/* DBus相关概念
 * DBus通信原理   : 两个进程收发任何消息都需要通过dbus-daemon进程进行处理和中转
 * address        : 地址 unix:path=/var/run/dbus/systemd_bus_socket,总线地址,local socket
 * connection name: 连接 com.company.myprocess,系统唯一,代表一个服务进程
 * object path    : 目标 /com/company/myprocess/TextFileManager
 * interface name : 接口 com.company.myprocess.TextFileManager
 * method name    : 方法 名字任意取
 *
 * DBus消息有四种 :
 * Method Call Message   --- 用于请求一个方法调用
 * Method Return Message --- 用于方法调用返回消息
 * Error Message         --- 当方法调用发生异常时返回错误
 * Signal Message        --- 通知进程一个指定的信号(事件)被触发
 */
/******************************************************************************
使用模板类实现DBus消息处理需注意以下几点:
1.子类定义时必需使用宏SDBUS_OBJECT_INIT,实现单例模式.
2.如果是服务端,子类必须定义静态的消息回调函数,用于填充vtable结构体
参考例程(服务端):
class Calculator:public SDBusObject<Calculator>{
	DECL_SDBUSOBJECT(Calculator)
	{
		//do init
	}
public:
    virtual ~Calculator(){};
	static int onMethodAdd(SDBusMessage* msg, void *userdata, SDBusError* err)//此函数作为sd_bus_message_handler_t回调函数
	{
		int x,y;
		Calculator& calc = Calculator::getInstance();
		calc.getMethodArgs(msg,"ii",&x,&y);
		calc.replyMethodReturn(msg,"i",x+y);
		return RC_OK;
	}
	static int onSignal(SDBusMessage* msg, void *userdata, SDBusError* err)
	{
		TRACE_INFO("receive signal.\n");
		return RC_OK;
	}
}
int main()
{
	SDBusConnection conn;
	if(conn.openUser()!=RC_OK)
	{
		return -1;
	}
    Calculator& calc = Calculator::getInstance();
	calc.init("/com/company/Calculator", "com.company.Calculator");

	#if 1	//信号接收
	calc.setSignalMatch(conn, "com.company.client", "/com/company/Calculator", "com.company.Calculator", "signal", &Calculator::onSignal);
	#endif

	#if 1
	SDBusVTable vtable[]={
	SDBusVTableRowStart(0),
	SDBusVTableRowMethod("add", "ii", "i", &Calculator::onMethodAdd),
	SDBusVTableRowEnd
	};
	calc.setVTable(conn,vtable,3);
	#endif
	
	conn.requestName("com.company.service");
	while(1)
	{
		conn.process(1000000);
	}
    return 0;
}
参考例程(客户端):
int main()
{
	SDBusConnection conn;
	if(conn.openUser()!=RC_OK)
	{
		TRACE_RED("cannot open user dbus!\n");
		return RC_ERROR;
	}
	SDBusObjectDummy& dummy = SDBusObjectDummy::getInstance();

	#if 1	//信号消息
	conn.requestName("com.company.client");
	dummy.emitSignal(conn, "/com/company/Calculator", "com.company.Calculator", "signal", NULL);
	#endif

	#if 1	//方法消息
	int x=100;
	int y=200;
	int sum;
   	dummy.callMethod(conn,true,"com.company.service", "/com/company/Calculator", "com.company.Calculator", "add", "ii",100,200);
   	dummy.getMethodReply("i", &sum);
   	TRACE_INFO("call add(%d,%d),reply=%d\n",x,y,sum);
   	#endif
   	conn.close();
}

******************************************************************************/
/* VTable的宏定义(源码在sd-bus-vatable.h中) */
#define SDBusVTableRowStart(flags)	SD_BUS_VTABLE_START(flags)
#define SDBusVTableRowMethod(name,param, result, handler)	SD_BUS_METHOD(name, param, result, handler, SD_BUS_VTABLE_UNPRIVILEGED)
#define SDBusVTableRowSignal(name, param, flags) SD_BUS_SIGNAL(name, param, flags)
#define SDBusVTableRowEnd	SD_BUS_VTABLE_END
using SDBus = sd_bus;
using SDBusVTable = sd_bus_vtable;
using SDBusSlot = sd_bus_slot;
using SDBusMessage = sd_bus_message;
using SDBusError = sd_bus_error;
/***********************************************************************
 * SDBusMessageHandler返回值说明:(参考sd_bus_add_match函数手册)
 * If an error occurs during the callback invocation, the callback
 * should return a negative error number. If it wants other callbacks
 * that match the same rule to be called, it should return 0. Otherwise
 * it should return a positive integer.
 * 返回0表示再回调一次,正常返回正整数!!!
 */
using SDBusMessageHandler = sd_bus_message_handler_t;

/* DBus连接 */
class SDBusConnection{
public:
	SDBusConnection(){};
	virtual ~SDBusConnection(){};
	int openUser(bool dflt=false);
	int openSystem(bool dflt=false);
	SDBus* getBus();
	void close();
	int bindObjectVTable(std::string path, std::string interface, SDBusVTable* vtable);
	int requestName(std::string name);
	int releaseName(std::string name);
	std::string getName();
	std::string getUniqueName();
	bool process(int usTimeout);/* 每调用一次,检查连接是否有新消息*/
private:
	SDBus* m_bus{NULL};
	SDBusSlot* m_slot{NULL};
	std::string m_name{""};
};

/* SDBusObject模板基类,单例,子类必须定义vtable中需要的回调函数:
 * typedef int (*sd_bus_message_handler_t)(sd_bus_message *m, void *userdata, sd_bus_error *ret_error);
 * typedef int (*sd_bus_property_get_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
 * typedef int (*sd_bus_property_set_t) (sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *value, void *userdata, sd_bus_error *ret_error);
 */
template<typename T>
class SDBusObject{
private:
	std::string m_path{""};
	std::string m_interface{""};
	SDBusVTable* m_vtable{NULL};
	SDBusMessage* m_reply{NULL};
	SDBusSlot* m_slot{NULL};
public:
	static T& getInstance()
	{
		static T instance;
		return instance;
	}
	virtual ~SDBusObject()
	{
		if(m_vtable) 
		{
			free(m_vtable);
			m_vtable=NULL;
		}
		sd_bus_slot_unref(m_slot);
	}
	void setDBusObject(std::string path, std::string interface)
	{
		m_path=path;
		m_interface = interface;
	}
	std::string path()
	{
		return m_path;
	}
	std::string interface()
	{
		return m_interface;
	}

	int setVTable(SDBusConnection& conn,SDBusVTable* vtable,int rows)
	{
		/* 复制一份vtable到SDBusObject中 */
		if(m_vtable)
		{
			free(m_vtable);
			m_vtable=NULL;
		}
		m_vtable = (SDBusVTable*)calloc(rows,sizeof(SDBusVTable));
		if (m_vtable==NULL)
		{
			TRACE_ERR_CLASS("cannot alloc vtable!\n");
			return RC_ERROR;
		}
		memcpy(m_vtable,vtable,rows*sizeof(SDBusVTable));
		return conn.bindObjectVTable(m_path, m_interface, m_vtable);			
	}

	/* 设置信号接收匹配规则 */
	int setSignalMatch(SDBusConnection& conn,std::string sender, std::string path,std::string interface,std::string signal,SDBusMessageHandler handler)
	{
		int rc = sd_bus_match_signal(conn.getBus(), &m_slot, CSTR(sender), CSTR(path), CSTR(interface), CSTR(signal), handler, NULL);
		if (rc<0)
		{
			TRACE_ERR_CLASS("signal match error:%s\n",ERRMSG(-rc));
		}
		return (rc<0)?RC_ERROR:RC_OK;
	}
	
	/* 调用Method(发送Method Call Message) */
	template<typename... args>
	int callMethod(SDBusConnection& conn,bool reply, std::string destination,std::string path, std::string interface, std::string method,const char* types,args... param)
	{
		SDBusError error = SD_BUS_ERROR_NULL;
		int rc = sd_bus_call_method(conn.getBus(), CSTR(destination), CSTR(path),	CSTR(interface),  CSTR(method),
								 &error, (reply?(&m_reply):NULL),  types, param...);
		if (rc < 0) 
		{
			TRACE_ERR_CLASS("call method error: %s\n", error.message);
		}
		sd_bus_error_free(&error);
		return (rc<0)?RC_ERROR:RC_OK;
	}

	/* 可变参数模板函数实现读取Method传入的参数 */
	template<typename... args>
	int getMethodArgs(SDBusMessage *msg,const char *types, args... param)
	{
		int rc = sd_bus_message_read(msg,types,param...);
		if (rc<0)
		{
			TRACE_ERR_CLASS("read message error:%s\n",ERRMSG(-rc));
		}
		return (rc<0)?RC_ERROR:RC_OK;
	}

	/* 发送Method Return Message,当一个方法没有返回值时,也必须调用此函数:replyMethodReturn(msg,NULL)
	 * 注意:sd_bus_reply_method_return成功是返回1的,在方法回调函数也必须返回1,如果返回0的话reply消息
	 * 会发送两次,最后那一次dbus-daemon会报告"Reject send message"错误!!!
	 */
	template<typename... args>
	int replyMethodReturn(SDBusMessage *msg,const char* types, args... param)
	{
		int rc = sd_bus_reply_method_return(msg,types,param...);
		if (rc<0)
		{
			TRACE_ERR_CLASS("reply_method_return error:%s\n",ERRMSG(-rc));
			return RC_ERROR;
		}
		return rc;
	}
	
	/* 发送Method Error Message */
	int replyMethodError(SDBusMessage *msg,const SDBusError* error)
	{
		int rc = sd_bus_reply_method_error(msg, error);
		if (rc<0)
		{
			TRACE_ERR_CLASS("reply_method_error error:%s\n",ERRMSG(-rc));
			return RC_ERROR;
		}
		return rc;
	}

	/* 获取Method返回值(接收Method Call Return Message) */
	template<typename... args>
	int getMethodReply(const char *types, args... param)
	{
		int rc = sd_bus_message_read(m_reply,types,param...);
		if (rc<0)
		{
			TRACE_ERR_CLASS("read message error:%s\n",ERRMSG(-rc));
		}
		sd_bus_message_unref(m_reply);
		return (rc<0)?RC_ERROR:RC_OK;
	}
	/* 触发信号(发送Signal Message),发送者不需要知道接收者,相当于广播,由接收者将要接收的信号注册到总线 */
	template<typename... args>
	int emitSignal(SDBusConnection& conn,std::string path, std::string interface, std::string signal,const char* types,args... param)
	{
		int rc = sd_bus_emit_signal(conn.getBus(), CSTR(path),	CSTR(interface),  CSTR(signal), types, param...);
		if (rc < 0) 
		{
			TRACE_ERR_CLASS("emit signal error: %s\n", ERRMSG(-rc));
		}
		return (rc<0)?RC_ERROR:RC_OK;
	}

protected:
	SDBusObject(){};
    SDBusObject(const SDBusObject& )=default;
	SDBusObject& operator=(const SDBusObject&)=default; 	
};

/* 子类构造宏,所有继承SDBusObject的子类在类定义起始处都必须使用该宏 */
#define DECL_SDBUSOBJECT(ClassName)	 private:friend class SDBusObject<ClassName>;ClassName()

/* 一个最简单的SDBusObject类 */
class SDBusObjectDummy:public SDBusObject<SDBusObjectDummy>{
	DECL_SDBUSOBJECT(SDBusObjectDummy)
	{
	}
};


#endif
